package org.quickserver.net.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
















public class BlockingUDPClient
  implements ClientService
{
  private static final Logger logger = Logger.getLogger(BlockingClient.class.getName());
  
  private SocketAddress address;
  
  private DatagramSocket socket;
  private DatagramPacket incoming;
  private DatagramPacket outgoing;
  private static int timeoutInSeconds = 5;
  private static String charset = "ISO-8859-1";
  
  private static boolean debug = false;
  private StringBuilder readLineBuffer = new StringBuilder();

  
  public static int getTimeoutInSeconds() { return timeoutInSeconds; }


  
  public static void setTimeoutInSeconds(int aTimeoutInSeconds) { timeoutInSeconds = aTimeoutInSeconds; }


  
  public static boolean isDebug() { return debug; }


  
  public static void setDebug(boolean aDebug) { debug = aDebug; }


  
  public int getMode() { return 1; }

  
  public void connect(String host, int port) throws Exception {
    this.socket = new DatagramSocket();
    this.address = new InetSocketAddress(host, port);
    this.socket.setSoTimeout(getTimeoutInSeconds() * 1000);
  }

  
  public boolean isConnected() { return this.socket.isConnected(); }

  
  public void close() throws IOException {
    if (this.socket != null) {
      this.socket.close();
      this.socket = null;
    } 
  }
  
  public void sendBytes(byte[] data) throws IOException {
    if (isDebug()) logger.log(Level.FINE, "Sending bytes: {0}", Integer.valueOf(data.length));
    
    this.outgoing = new DatagramPacket(data, data.length, this.address);
    if (this.socket == null) {
      throw new IOException("socket is null");
    }
    this.socket.send(this.outgoing);
  }
  
  public void sendBytes(String data, String charset) throws IOException {
    if (data == null || "".equals(data)) {
      throw new IOException("data is null or blank");
    }
    sendBytes(data.getBytes(charset));
  }
  
  public void sendLine(String data, String charset) throws IOException {
    String outData = String.valueOf(data) + "\r\n";
    sendBytes(outData, charset);
  }
  
  public void sendObject(Object data) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(data);
    oos.flush();
    sendBytes(bos.toByteArray());
  }
  
  private byte[] readBinary() throws IOException {
    byte[] buf = new byte[8192];
    
    this.incoming = new DatagramPacket(buf, buf.length);
    this.socket.receive(this.incoming);
    
    if (this.incoming.getData() == null) {
      return null;
    }
    int incomingLen = this.incoming.getLength();
    byte[] inData = new byte[incomingLen];
    System.arraycopy(this.incoming.getData(), 0, inData, 0, incomingLen);
    return inData;
  }
  
  public byte[] readBytes() throws IOException {
    byte[] inData = readBinary();
    if (inData == null) {
      return null;
    }
    return inData;
  }

  
  public String readBytes(String charset) throws IOException { return new String(readBytes(), charset); }

  
  public String readLine() throws IOException {
    int idx = -1;
    while (true) {
      idx = this.readLineBuffer.indexOf("\r\n");
      if (idx == -1) {
        byte[] inData = readBinary();
        this.readLineBuffer.append(new String(inData, charset));
        
        continue;
      } 
      break;
    } 
    String data = this.readLineBuffer.substring(0, idx);
    this.readLineBuffer.delete(0, idx + 2);
    
    return data;
  }
  
  public Object readObject() throws IOException, ClassNotFoundException {
    byte[] inData = readBinary();
    ObjectInputStream oos = null;
    Object obj = null;
    if (inData != null) {
      oos = new ObjectInputStream(new ByteArrayInputStream(inData));
      obj = oos.readObject();
    } 
    return obj;
  }


  
  public Socket getSocket() { throw new UnsupportedOperationException("Not supported for UDP"); }

  
  public byte[] sendAndReceiveBinary(String host, int port, byte[] data) throws Exception {
    connect(host, port);
    sendBytes(data);
    byte[] response = readBinary();
    return response;
  }
  
  public String sendAndReceiveBytes(String host, int port, String data) throws Exception {
    connect(host, port);
    sendBytes(data, charset);
    byte[] respBytes = readBytes();
    String response = new String(respBytes);
    return response;
  }
  
  public String sendAndReceiveLine(String host, int port, String data) throws Exception {
    connect(host, port);
    sendLine(data, charset);
    String response = readLine();
    return response;
  }
  
  public Object sendAndReceiveObject(String host, int port, Object data) throws Exception {
    connect(host, port);
    sendObject(data);
    Object response = readObject();
    return response;
  }

  
  public int readByte() throws IOException { throw new UnsupportedOperationException("Not supported yet."); }


  
  public void sendByte(int data) throws IOException { throw new UnsupportedOperationException("Not supported yet."); }
}
